﻿#region Copyright(C) 2013-2013 NCS Pte. Ltd. All rights reserved.
// ==================================================================================================
// Copyright(C) 2010 NCS Pte. Ltd. All rights reserved.
//
// SYSTEM NAME      : iConnect .Net Application Framework
// COMPONENT ID     : NCS.IConnect.Core.EntLib
// COMPONENT DESC   : Microsoft Enterprise Library Adapter.
//
// CREATED DATE/BY  : 18 Dec 2013 / Jiang Jin Nan
//
// REVISION HISTORY :
// DATE/BY            SR#/CS/PM#/OTHERS       DESCRIPTION OF CHANGE
// ==================================================================================================
#endregion

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using Microsoft.Practices.Unity;
using Microsoft.Practices.Unity.Configuration;
using Microsoft.Practices.Unity.Utility;
using NCS.IConnect.Core.Configuration;
using NCS.IConnect.Core.IoC.Configuration;

namespace NCS.IConnect.Core.IoC
{
    /// <summary>
    /// The unity based service locator.
    /// </summary>
    [ConfigurationElement(typeof(UnityContainerServiceLocatorData))]
    [MapTo(typeof(IServiceLocator))]
    public class UnityContainerServiceLocator: ServiceLocatorBase
    {
        /// <summary>
        /// Gets the unity container.
        /// </summary>
        /// <value>
        /// The unity container.
        /// </value>
        public IUnityContainer UnityContainer { get; private set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="UnityContainerServiceLocator"/> class.
        /// </summary>
        public UnityContainerServiceLocator()
        {
            this.UnityContainer = new UnityContainer();
            this.UnityContainer.AddNewExtension<DefaultInjectionExtension>();
            UnityConfigurationSection settings;
            if (ConfigManager.TryGetConfigurationSection<UnityConfigurationSection>(UnityConfigurationSection.SectionName, out settings) &&
                settings.Containers.Any(c=>string.IsNullOrEmpty(c.Name)))
            {
                settings.Configure(this.UnityContainer);
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="UnityContainerServiceLocator"/> class.
        /// </summary>
        /// <param name="unityContainer">The unity container.</param>
        public UnityContainerServiceLocator(IUnityContainer unityContainer)
        {
            Guard.ArgumentNotNull(unityContainer, "unityContainer");
            unityContainer.AddExtension(new DefaultInjectionExtension());
            this.UnityContainer = unityContainer;
        }

        /// <summary>
        /// Gets the service based on the specified registered type and optional registration name.
        /// </summary>
        /// <param name="registeredType">The service type registered.</param>
        /// <param name="name">The service type registration name.</param>
        /// <returns>
        /// The service instance activated.
        /// </returns>
        public override object GetService(Type registeredType, string name = null)
        {
            Guard.ArgumentNotNull(registeredType, "registeredType");
            try
            {
                if (string.IsNullOrEmpty(name))
                {
                    string defaultName;
                    if (this.UnityContainer.Configure<DefaultInjectionExtension>().TryGetDefaultName(registeredType, out defaultName))
                    {
                        name = defaultName;
                    }
                }
                return string.IsNullOrWhiteSpace(name) ? this.UnityContainer.Resolve(registeredType) : this.UnityContainer.Resolve(registeredType, name);
            }
            catch (ResolutionFailedException ex)
            {
                throw new ResolutionException(ex.Message, ex);
            }
        }

        /// <summary>
        /// Gets all services based on the specified registered type.
        /// </summary>
        /// <param name="registeredType">The service type registered.</param>
        /// <returns>
        /// The list of acticated service instances.
        /// </returns>
        public override IEnumerable<object> GetAllServices(Type registeredType)
        {
            Guard.ArgumentNotNull(registeredType, "registeredType");
            try
            {
                return this.UnityContainer.ResolveAll(registeredType);
            }
            catch (ResolutionFailedException ex)
            {
                throw new ResolutionException(ex.Message, ex);
            }
        }

        /// <summary>
        /// Gets all keys (service type registrtion name) for the specified registered type.
        /// </summary>
        /// <param name="registeredType">The service type registered.</param>
        /// <returns>
        /// The key list.
        /// </returns>
        public override IEnumerable<string> GetAllKeys(Type registeredType)
        {
            Guard.ArgumentNotNull(registeredType, "registeredType");
            return this.UnityContainer.Registrations
                .Where(registration => registration.RegisteredType == registeredType)
                .Select(registration => registration.Name)
                .ToArray();
        }

        /// <summary>
        /// Determines whether the specified registered type is registered.
        /// </summary>
        /// <param name="registeredType">The service type registered.</param>
        /// <returns>
        ///   <c>true</c> if the specified registered type is registered; otherwise, <c>false</c>.
        /// </returns>
        public override bool IsRegistered(Type registeredType)
        {
            Guard.ArgumentNotNull(registeredType, "registeredType");
            return this.UnityContainer.Registrations.Any(registration => registration.RegisteredType == registeredType);
        }

        /// <summary>
        /// Determines whether the specified registered type is registered.
        /// </summary>
        /// <param name="registeredType">The service type registered.</param>
        /// <param name="mappedToType">The concrete type to which the registered type is mapped.</param>
        /// <returns>
        ///   <c>true</c> if the specified registered type is registered; otherwise, <c>false</c>.
        /// </returns>
        public override bool IsRegistered(Type registeredType, Type mappedToType)
        {
            Guard.ArgumentNotNull(registeredType, "registeredType");
            Guard.ArgumentNotNull(mappedToType, "mappedToType");
            return this.UnityContainer.Registrations.Any(registration => registration.RegisteredType == registeredType && registration.MappedToType == mappedToType);
        }        

        /// <summary>
        /// Perform service type registration.
        /// </summary>
        /// <param name="registeredType">The service type registered.</param>
        /// <param name="mappedToType">The concrete type to which the registered type is mapped.</param>
        /// <param name="name">The service type registration name.</param>
        /// <param name="isDefault">if set to <c>true</c> [is default].</param>
        /// <param name="lifetime">The lifetime.</param>
        public override void Register(Type registeredType, Type mappedToType, string name = null, bool isDefault = false, Lifetime lifetime = Lifetime.Transient)
        {
            Guard.ArgumentNotNull(registeredType, "registeredType");
            Guard.ArgumentNotNull(mappedToType, "mappedToType");

            LifetimeManager lifetimeManager = lifetime == Lifetime.Singleton ? (LifetimeManager)new ContainerControlledLifetimeManager(): (LifetimeManager)new TransientLifetimeManager();
            if (isDefault)
            {
                this.UnityContainer.Configure<DefaultInjectionExtension>().Register(registeredType, name);
            }
            this.UnityContainer.RegisterType(registeredType, mappedToType, name, lifetimeManager);
        }

        /// <summary>
        /// Registers the specified new expression.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="creator">The new expression.</param>
        /// <param name="name">The name.</param>
        /// <param name="isDefault">if set to <c>true</c> [is default].</param>
        /// <param name="lifetime">The lifetime.</param>
        public override void Register<T>(Func<T> creator, string name = null, bool isDefault = false, Lifetime lifetime = Lifetime.Transient)
        {
            Guard.ArgumentNotNull(creator, "creator");
            if (isDefault)
            {
                this.UnityContainer.Configure<DefaultInjectionExtension>().Register<T>(name);
            }
            LifetimeManager lifetimeManager = lifetime == Lifetime.Singleton ? (LifetimeManager)new ContainerControlledLifetimeManager() : (LifetimeManager)new TransientLifetimeManager();
            this.UnityContainer.RegisterType(typeof(T), name, lifetimeManager, new InjectionFactory(container => creator()));
        }
    }
}